package com.huitone.smspfm.socket.client;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ConnectException;
import java.net.Socket;
import java.util.Date;

import org.apache.commons.codec.binary.Base64;
import org.apache.log4j.LogManager;
import org.apache.log4j.Logger;

import com.bin.common.utils.DateTimeUtils;
import com.bin.common.utils.StringUtils;
import com.huitone.smspfm.socket.core.AbstractMessageHandler;
import com.huitone.smspfm.socket.core.DefaultMessageHandler;
import com.huitone.smspfm.socket.core.SocketHolder;
import com.huitone.smspfm.socket.exception.MyConnectException;
import com.huitone.smspfm.socket.protocol.sgip.constant.CommandID;
import com.huitone.smspfm.socket.protocol.sgip.constant.ErrorCode;
import com.huitone.smspfm.socket.protocol.sgip.message.Message;
import com.huitone.smspfm.socket.protocol.sgip.message.msgbody.Bind;
import com.huitone.smspfm.socket.protocol.sgip.message.msgbody.Bind.LoginType;
import com.huitone.smspfm.socket.protocol.sgip.message.msgbody.BindResp;
import com.huitone.smspfm.socket.protocol.sgip.message.msgbody.Deliver;
import com.huitone.smspfm.socket.protocol.sgip.message.msgbody.IBody;
import com.huitone.smspfm.socket.protocol.sgip.message.msgbody.Report;
import com.huitone.smspfm.socket.protocol.sgip.message.msgbody.Report2;
import com.huitone.smspfm.socket.protocol.sgip.message.msgbody.Submit;
import com.huitone.smspfm.socket.protocol.sgip.message.msgbody.Submit2;
import com.huitone.smspfm.socket.protocol.sgip.message.msghead.Head;
import com.huitone.smspfm.socket.utils.SerialUtils;
import com.huitone.smspfm.socket.utils.SocketUtils;

/**   
* @Title SocketClient.java 
* @Package com.huitone.smspfm.socket.client 
* @Description TODO(用一句话描述该文件做什么) 
* @author ybliang   
* @date 2017年3月30日 下午2:32:22 
* @version V1.0   
*/
public class SocketClient2 {
	
	private static final Logger logger = LogManager.getLogger(SocketClient2.class);
	
	private SocketHolder socketHolder = null;
	
	private static SocketClient2 instance = null;
	
	private AbstractMessageHandler messageHandler = new DefaultMessageHandler();
	
	private int serverPort = 8901;
	
	private String serverIp = null;
	
	private String loginName = null;
	
	private String loginPassword = null;
	
	private String srvNode = null;
	
	private LoginType loginType = LoginType.CLIENT_TO_PLATFORM;
	
	private int maxResendCount = 1;
	
	private int coderule = 0;
	
	private int soTimeOut = 30000;
	
	private Thread shRunner = null;
	
	private boolean forServer = false;
	
	private static long lastTime = System.currentTimeMillis();
	
	private static long lastCount = 0;
	
	public static SocketClient2 getInstance() {
		if (instance == null) {
			synchronized (SocketClient2.class) {
				if (instance == null) {
					instance = new SocketClient2();
				}
			}
		}
		return instance;
	}
	
	public SocketClient2() {}
	
	private synchronized boolean bind() throws IOException {
		
		boolean loginSuccess = false;
		
		Socket socket = null;
		try {
			socket = new Socket(serverIp, serverPort);
			socket.setKeepAlive(true);
			socket.setTcpNoDelay(true);
			socket.setSendBufferSize(64*1024);
		} catch (ConnectException e) {
			throw new MyConnectException(e.getMessage(), srvNode, serverIp, serverPort);
		}
		
		OutputStream outputStream = socket.getOutputStream();
		InputStream inputStream = socket.getInputStream();
		
		Bind bind = Bind.build(srvNode, loginType.getValue(), loginName, loginPassword);
		Head head = Head.build(bind.length(), CommandID.SGIP_BIND, coderule, 
				StringUtils.toIntegerP(DateTimeUtils.dateToStr(new Date(), "MMddHHmmss")), SerialUtils.getSerial());
		
		boolean writed = SocketUtils.write(Message.build(head, bind), outputStream);
		
		if (writed) {
			
			Message message = SocketUtils.read(inputStream);
			if (message != null) {
				IBody body = message.getBody();
				if (body != null && body instanceof BindResp) {
					BindResp bindResp = (BindResp) body;
					int result = bindResp.getResult();
					if (result == ErrorCode.CODE0) {
						loginSuccess = true;
					}
					logger.info("创建连接:" + ErrorCode.codeDesc(result));
				} else {
					logger.warn("创建连接时返回了错误的消息类型,绑定失败");
				}
			} else {
				logger.warn("创建连接时返回了空消息,绑定失败");
			}
			
			if (loginSuccess) {
				if (socketHolder != null) {
					socketHolder.close();
					socketHolder = null;
				}
				if (shRunner != null) {
					shRunner.interrupt();
					shRunner = null;
				}
				shRunner = new Thread(socketHolder = new SocketHolder(socket, messageHandler).setForServer(forServer).setSoTimeOut(soTimeOut));
				shRunner.start();
				
				logger.debug("通讯连接已创建");
				
			} else {
				try {
					socket.shutdownInput();
					socket.shutdownOutput();
					socket.close();
					socket = null;
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}
		
		return loginSuccess;
	}
	
	public synchronized boolean sendMsg(Head head, Submit submit) throws IOException {
		
		boolean sendstatus = false;
		int resendCount = 0;
		
		do {
			try {
				if (socketHolder != null && socketHolder.getAccept() != null) {
					socketHolder.write(Message.build(head, submit));
					sendstatus = true;
				} else {
					bind();
				}
			} catch (IOException e) {
				logger.error(e.getMessage()); break;
				/*try {
					bind();
				} catch (IOException e2) {
					logger.error(e2.getMessage());
				}*/
			}
			
			//resendCount ++;
			
		} while (!sendstatus && resendCount ++ < maxResendCount);
		
		speeds();
		
		return sendstatus;
	}
	
	public synchronized boolean sendMsg(Head head, Submit2 submit) throws IOException {
		
		boolean sendstatus = false;
		
		int resendCount = 0;
		
		do {
			try {
				if (socketHolder != null && socketHolder.getAccept() != null) {
					socketHolder.write(Message.build(head, submit));
					sendstatus = true;
				} else {
					bind();
				}
			} catch (IOException e) {
				logger.error(e.getMessage()); break;
				/*try {
					bind();
				} catch (IOException e2) {
					logger.error(e2.getMessage());
				}*/
			}
			
			//resendCount ++;
			
		} while (!sendstatus && resendCount ++ < maxResendCount);
		
		return sendstatus;
	}
	
	public synchronized boolean sendReport(Head head, Report report) {
		
		boolean sendstatus = false;
		
		int resendCount = 0;
		
		do {
			try {
				if (socketHolder != null && socketHolder.getAccept() != null) {
					socketHolder.write(Message.build(head, report));
					sendstatus = true;
				} else {
					bind();
				}
			} catch (IOException e) {
				logger.error(e.getMessage()); break;
				/*try {
					bind();
				} catch (IOException e2) {
					logger.error(e2.getMessage());
				}*/
			}
			
			//resendCount ++;
			
		} while (!sendstatus && resendCount ++ < maxResendCount);
		
		return sendstatus;
	}
	
	public synchronized boolean sendReport2(Head head, Report2 report) {
		
		boolean sendstatus = false;
		
		int resendCount = 0;
		
		do {
			
			try {
				if (socketHolder != null && socketHolder.getAccept() != null) {
					socketHolder.write(Message.build(head, report));
					sendstatus = true;
				} else {
					bind();
				}
			} catch (IOException e) {
				logger.error(e.getMessage()); break;
			}
			
		} while (!sendstatus && resendCount ++ < maxResendCount);
		
		return sendstatus;
	}
	
	public synchronized boolean sendDeliver(Head head, Deliver deliver) {
		
		boolean sendstatus = false;
		int resendCount = 0;
		
		do {
			try {
				if (socketHolder != null && socketHolder.getAccept() != null) {
					socketHolder.write(Message.build(head, deliver));
					sendstatus = true;
				} else {
					bind();
				}
			} catch (IOException e) {
				logger.error(e.getMessage()); break;
			}
			
			//resendCount ++;
			
		} while (!sendstatus && resendCount ++ < maxResendCount);
		
		return sendstatus;
	}
	
	public void shutdown() {
		
		if (socketHolder != null) {
			socketHolder.close();
			socketHolder = null;
		}
		
		if (shRunner != null) {
			shRunner.interrupt();
			shRunner = null;
		}
	}
	
	public SocketClient2 setMessageHandler(AbstractMessageHandler messageHandler) {
		this.messageHandler = messageHandler;
		return this;
	}

	public SocketClient2 setServerPort(int serverPort) {
		this.serverPort = serverPort;
		return this;
	}

	public SocketClient2 setServerIp(String serverIp) {
		this.serverIp = serverIp;
		return this;
	}

	public SocketClient2 setLoginName(String loginName) {
		this.loginName = loginName;
		return this;
	}

	public SocketClient2 setLoginPassword(String loginPassword) {
		this.loginPassword = Base64.encodeBase64String(StringUtils.toString(loginPassword).getBytes());
		return this;
	}

	public SocketClient2 setCoderule(int coderule) {
		this.coderule = coderule;
		return this;
	}

	public SocketClient2 setSoTimeOut(int soTimeOut) {
		this.soTimeOut = soTimeOut;
		return this;
	}

	public SocketClient2 setSrvNode(String srvNode) {
		this.srvNode = srvNode;
		return this;
	}

	public SocketClient2 setLoginType(LoginType loginType) {
		this.loginType = loginType;
		return this;
	}

	public SocketClient2 setForServer(boolean forServer) {
		this.forServer = forServer;
		return this;
	}
	
	public synchronized void speeds() {
		
		lastCount += 1;
		
		long currentTimeMillis = System.currentTimeMillis();
		
		if ((currentTimeMillis - lastTime) >= 1000) {
			logger.fatal("客户端发送短信速度(条/s):" + lastCount);
			lastCount = 0;
			lastTime = System.currentTimeMillis();
		}
		
	}

	public SocketHolder getSocketHolder() {
		return socketHolder;
	}
	
	public void intterupt() {
		
		System.out.println("线程名:" + Thread.currentThread().getName());
		
		Thread.currentThread().getThreadGroup().interrupt();
	}
}
